﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500
    /// <summary>
    /// 值类型柔性数组
    /// </summary>
    /// <typeparam name="T">元素类型</typeparam>
    unsafe public partial class ValueTypeFlexibleArray<T> : IDisposable, IEnumerable<T> where T : struct
    {
        private void ReallocArray(int length)
        {
            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"元数个数不能小于0,值\"{length}\"无效.");
            }

            if (this.IsReferenced())
            {
                throw new InvalidOperationException("当前数组模式为引用模式,不允许对内存大小进行调整操作.");
            }

            if (this._capcity < length)
            {
                var byteCount = (nuint)(length * sizeof(T));
                void* newPtr;
                if (this._bufferPtr == null)
                {
                    newPtr = NativeMemory.Alloc(byteCount);
                }
                else
                {
                    newPtr = NativeMemory.Realloc(this._bufferPtr, byteCount);
                }

                if (newPtr == null)
                {
                    throw new AccessViolationException($"内存指针元素个数从{this._capcity}-{length}申请或调整内存失败");
                }

                this._bufferPtr = (T*)newPtr;
                this._capcity = length;
            }
        }


        /// <summary>
        /// 清除当前对象数据指针指向的内容
        /// </summary>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Clear()
        {
            if (this._bufferPtr != null)
            {
                NativeMemory.Clear(this._bufferPtr, (nuint)(this._length * sizeof(T)));
            }
        }

        /// <summary>
        /// Copies the contents of this span into a new array.  This heap
        /// allocates, so should generally be avoided, however it is sometimes
        /// necessary to bridge the gap with APIs written in terms of arrays.
        /// </summary>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public T[] ToArray()
        {
            if (this._length <= 0)
            {
#if NET4_0
                return UtilZ.Dotnet.Ex.Base.ArrayEx.Empty<T>();
#else
                return Array.Empty<T>();
#endif
            }

            var destination = new T[this._length];
            fixed (T* dstPtr = destination)
            {
                NativeMemory.Copy(this._bufferPtr, dstPtr, (nuint)(this._length * sizeof(T)));
            }

            return destination;
        }





        /// <summary>
        /// 将当前数据复制到目标数据中,作参数检查
        /// </summary>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝数据长度</returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(ValueTypeFlexibleArray<T> destination)
        {
            if (this._length > destination._length)
            {
                throw new ArgumentOutOfRangeException("目标长度不足");
            }

            int copyCount = Math.Min(this._length, destination.Length);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr, destination._bufferPtr, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将当前数据复制到目标数据中,作参数检查
        /// </summary>
        /// <param name="startIndex">当前数据起始位置</param>
        /// <param name="destination">目标数据</param>
        /// <param name="destinationStartIndex">目标数据起始位置</param>
        /// <param name="count">复制元素个数</param>
        /// <returns>拷贝数据长度</returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(int startIndex, ValueTypeFlexibleArray<T> destination, int destinationStartIndex, int count)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"要拷贝的数据长度{count}无效,该值不能小于0.");
            }

            if (startIndex < 0 || startIndex + count > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"起始索引值{startIndex}无效或数据长度{count}无效,该值不能小于0或大于当前数据长度{this._length}.");
            }

            if (destinationStartIndex < 0 || destinationStartIndex + count > destination._length)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"目标索引值{startIndex}无效或数据长度{count}无效,该值不能小于0或大于目标数据长度{destination._length}.");
            }

            var availableLen = this._length - startIndex;
            int copyCount = Math.Min(count, availableLen);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr + startIndex, destination._bufferPtr + destinationStartIndex, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标指针数据中,作参数检查
        /// </summary>
        /// <param name="destination">目标数据指针</param>
        /// <param name="count">指针数据元素个数</param>
        /// <returns>拷贝数据长度</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(T* destination, int count)
        {
            if (count < 0 || count > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(count), $"目标长度{count}大于源长度{this._length - count}.");
            }

            int copyCount = Math.Min(count, this._length);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr, destination, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标指针数据中,作参数检查
        /// </summary>
        /// <param name="startIndex">当前数据起始位置</param>
        /// <param name="destination">目标数据指针</param>
        /// <param name="count">指针数据元素个数</param>
        /// <returns>拷贝数据长度</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(int startIndex, T* destination, int count)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"要拷贝的数据长度{count}无效,该值不能小于0.");
            }

            if (startIndex < 0 || startIndex + count > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"起始索引值{startIndex}无效,该值不能小于0或大于当前数据长度{this._length}.");
            }

            var availableLen = this._length - startIndex;
            int copyCount = Math.Min(count, availableLen);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr + startIndex, destination, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标数据中,作参数检查
        /// </summary>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝数据长度</returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(SpanZ<T> destination)
        {
            int copyCount = Math.Min(this._length, destination.Length);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr, destination.DataPtr, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标数据中,作参数检查
        /// </summary>
        /// <param name="startIndex">当前数据起始位置</param>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝数据长度</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(int startIndex, SpanZ<T> destination)
        {
            if (startIndex < 0 || startIndex > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"起始索引值{startIndex}无效,该值不能小于0或大于当前数据长度{this._length}.");
            }

            int count = this._length - startIndex;
            if (count > destination.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destination.Length), $"目标长度{destination.Length}小于源长度{count}.");
            }

            int copyCount = Math.Min(count, destination.Length);
            int byteCount = copyCount * sizeof(T);
            NativeMemory.Copy(this._bufferPtr + startIndex, destination.DataPtr, (nuint)byteCount);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标数据中,作参数检查
        /// </summary>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝数据长度</returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(Span<T> destination)
        {
            int copyCount = Math.Min(this._length, destination.Length);
            new Span<T>(this._bufferPtr, copyCount).CopyTo(destination);
            return copyCount;
        }

        /// <summary>
        /// 将数据拷贝到目标数据中,作参数检查
        /// </summary>
        /// <param name="startIndex">当前数据起始位置</param>
        /// <param name="destination">目标数据</param>
        /// <returns>拷贝数据长度</returns>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public int CopyTo(int startIndex, Span<T> destination)
        {
            if (startIndex < 0 || startIndex > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(startIndex), $"起始索引值{startIndex}无效,该值不能小于0或大于当前数据长度{this._length}.");
            }

            int count = this._length - startIndex;
            if (count > destination.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destination.Length), $"目标长度{destination.Length}小于源长度{this._length - startIndex}.");
            }

            int copyCount = Math.Min(count, destination.Length);
            new Span<T>(this._bufferPtr + startIndex, copyCount).CopyTo(destination);
            return copyCount;
        }






        /// <summary>
        /// 从指针数据源中将数据复制到当前数组中,作参数检查
        /// </summary>
        /// <param name="sourcePtr">指针数据源</param>
        /// <param name="count">从指针数据源中复制的元素个数</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void CopyFrom(T* sourcePtr, int count)
        {
            if (count > this._length)
            {
                throw new ArgumentOutOfRangeException($"源长度{count}大于目标长度{this._length}不足");
            }

            int byteCount = count * sizeof(T);
            NativeMemory.Copy(sourcePtr, this._bufferPtr, (nuint)byteCount);
        }

        /// <summary>
        /// 从数据源中将数据复制到当前数组中,作参数检查
        /// </summary>
        /// <param name="source">数据源</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void CopyFrom(SpanZ<T> source)
        {
            if (source.Length > this._length)
            {
                throw new ArgumentOutOfRangeException($"源长度{source.Length}大于目标长度{this._length}不足");
            }

            int byteCount = source.Length * sizeof(T);
            NativeMemory.Copy(source.DataPtr, this._bufferPtr, (nuint)byteCount);
        }

        /// <summary>
        /// 从数据源中将数据复制到当前数组中,作参数检查
        /// </summary>
        /// <param name="source">数据源</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void CopyFrom(Span<T> source)
        {
            if (source.Length > this._length)
            {
                throw new ArgumentOutOfRangeException($"源长度{source.Length}大于目标长度{this._length}不足");
            }

            source.CopyTo(new Span<T>(this._bufferPtr, this._length));
        }





        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this._disposed)
            {
                return;
            }
            this._disposed = true;

            if (this.IsIndependent() && this._bufferPtr != null)
            {
                NativeMemory.Free(this._bufferPtr);
                this._bufferPtr = null;
                this._length = 0;
                this._capcity = 0;
            }
        }

    }
}
